using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using ICSharpCode.AvalonEdit.Highlighting;

namespace DesignPattern.Controls
{
    //CodeEditor是用户控件(UserControl)，自带XAML定义和资源，不需要全局注册
    //CodeEditor只是包装了AvalonEdit组件，通过命名空间引用即可使用，不需要额外样式
    //CodeEditor只需要在使用它的XAML文件中添加命名空间引用(xmlns:controls="...")即可直接使用
    public partial class CodeEditor : UserControl
    {
        public CodeEditor()
        {
            InitializeComponent();
            // 确保文本编辑器是只读的
            CodeTextEditor.IsReadOnly = true;
            // 添加鼠标滚轮事件处理(将控件的鼠标滚轮传递给父控件，让页面能够正常滚动  )
            this.PreviewMouseWheel += CodeEditor_PreviewMouseWheel;
            // 禁用鼠标点击(防止CodeTextEditor默认点击事件胡乱跳转)
            // 标记为True，冒泡事件就不会继续向上传递了
            CodeTextEditor.PreviewMouseDown += (s, e) => e.Handled = true;
        }
        // 处理鼠标滚轮事件，将滚轮事件传递给父容器
        private void CodeEditor_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            // 创建一个新的鼠标滚轮事件，传递给父控件
            var newEvent = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
            {
                RoutedEvent = UIElement.MouseWheelEvent,
                Source = this
            };
            // 获取父级ScrollViewer或其他可滚动容器
            var parent = VisualTreeHelper.GetParent(this);
            while (parent != null && !(parent is ScrollViewer) && !(parent is ItemsControl))
            {
                parent = VisualTreeHelper.GetParent(parent);
            }
            // 如果找到可滚动的父容器，发送事件
            if (parent != null)
            {
                ((UIElement)parent).RaiseEvent(newEvent);
                e.Handled = true; // 标记原事件为已处理，防止重复处理
            }
        }
        // 包装的原因是CodeTextEditor.Text不是一个依赖属性，无法使用Binding
        // 创建一个自定义控件封装原控件，并添加一个新的依赖属性，然后在属性变更回调中手动设置原控件的属性值。
        // 在ViewModel的属性绑定新建的依赖属性
        // 其他的临时解决方案，可以用AttachedProperty或EventTrigger，但是都不太优雅
        public static readonly DependencyProperty CodeTextProperty =
            DependencyProperty.Register(
                nameof(CodeText),
                typeof(string),
                typeof(CodeEditor),
                new PropertyMetadata(string.Empty, OnCodeTextChanged));
        public string CodeText
        {
            get => (string)GetValue(CodeTextProperty);
            set => SetValue(CodeTextProperty, value);
        }
        private static void OnCodeTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is CodeEditor codeEditor)
            {
                string codeText = e.NewValue as string ?? string.Empty;
                codeEditor.CodeTextEditor.Text = codeText;
            }
        }
    }
}